#ifndef _M_THREAD_H_
#define _M_THREAD_H_

// boost multi-threading headers
#include <boost/thread.hpp>

#include "./text.h"

#include "./mathematics/math_function.h"

//#ifdef ALLOW_DEBUGS
#define DEBUG_M_THREAD
//#endif

//! multi-threading
/*!
	namespace for multi-threading common routines
*/
namespace MThread {
	using namespace boost;
	using namespace Text;
	using namespace Mathematics::Function;

	/*! variable for holding the value of available cores */
	const usint num_core = std::max( 1, (int) boost::thread::hardware_concurrency() );
//	extern usint num_core;

	//! Sincrony and mutual exclusion class for critical regions.
	/*!
		Class for semaphore use. Not only binary.
	*/
	class Semaphore {
	private:
		/*! number of available resources */
		int n;
		/*! mutex for n */
		mutex m;
		
	public:
		/*!
			Semaphore common constructor.
			\param available max availability of a resource.
		*/
		Semaphore( const usint &available = 1 );
		//! Copy constructor
		Semaphore( const Semaphore &original );
		//! Destructor
		~Semaphore();
		
		/*!
			Tests if there are available resources and the flow can continue.
			\sa void Signal()
			\code
			sem_test.Wait(); // Semaphore sem_test();
			// ... uses the critical resource
			sem_test.Signal();
			\endcode
		*/
		void Wait();
		/*!
			Indicates that the resource is no longer being used.
			\sa void Wait()
			\code
			sem_test.Wait(); // Semaphore sem_test();
			// ... uses the critical resource
			sem_test.Signal();
			\endcode
		*/
		void Signal();
		
	};

	/*! semaphore for mutual exclusion on cout */
	extern Semaphore *sem_cout;

	class Functor{
	public:
		Functor(){};
		inline virtual void operator () (){
		};
	};
	//! Functor holder class
	/*!
		Holds a Functor. This is for thread calling. Resolves the virtual override address.
	*/
	class FunctorHolder {
	protected:
		/*! Functor to be called */
		Functor *func;
	public:
		/*!
			FunctorHolder common constructor.
			\param f The functor to be stored an called later.
		*/
		FunctorHolder( Functor *f );
		//! Copy constructor
		FunctorHolder( const FunctorHolder &original );
		//! Destructor
		~FunctorHolder();
		
		//! Calls (*func)(). Resolves the virtual adrress.
		inline void operator() () { func->operator()(); };
	};

	/*!
		Function for launching threads. Prevents enormous number of threads running
		at same time.
		
		\param routines Deque of functors to be called as operator().
		\param simult_thread The number of threads that can be running simultaneously.
		
		\sa void ThreadLauncher( Functor* routine, const uint &max_thread, const uint &simult_thread )
		
	*/
	void ThreadLauncher( const deque< Functor* > &routines, const uint &simult_thread = 2*num_core );
	/*!
		Function for launching threads. Prevents enormous number of threads running
		at same time.
		
		\param routine The functor to be called as operator().
		\param max_thread The number of threads to be created.
		\param simult_thread The number of threads that can be running simultaneously.
		
		\sa void ThreadLauncher( const deque< Functor* > &routines, const uint &simult_thread )
		
	*/
	template< class Func >
		void ThreadLauncher( const Func &routine, const uint &max_thread, const uint &simult_thread  = 2*num_core ){
	
			deque< thread* > t_list;
	
			for( usint i = 0; i < max_thread; ++i ){ // launching
				if( t_list.size() >= simult_thread ){ // avoids too many threads running
					t_list.front()->join();
	#ifdef DEBUG_M_THREAD
					sem_cout->Wait();
					cout << "join: " << i-t_list.size() << endl;
					sem_cout->Signal();
	#endif
					t_list.pop_front();
				}
	
	#ifdef DEBUG_M_THREAD
				sem_cout->Wait();
				cout << "launching: " << i << endl;
				sem_cout->Signal();
	#endif
	
				// creates and stores the new thread
				t_list.push_back(
					new thread(
						routine
					)
				);
	
	#ifdef DEBUG_M_THREAD
				sem_cout->Wait();
				cout << "launched: " << i << endl;
				sem_cout->Signal();
	#endif
			}
	
			while( t_list.size() > 0 ){ // joining
				t_list.front()->join();
				t_list.pop_front();
	#ifdef DEBUG_M_THREAD
				sem_cout->Wait();
				cout << "join: " << max_thread-t_list.size()-1 << endl;
				sem_cout->Signal();
	#endif
			}
	
	#ifdef DEBUG_M_THREAD
			cout << "Single!" << endl;
	#endif
	
		};

};

#endif
